package aca

import aca.SimpleChecker
import groovy.xml.MarkupBuilder
import groovy.xml.StreamingMarkupBuilder
import groovy.xml.XmlUtil
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.web.multipart.MultipartHttpServletRequest
import org.springframework.web.multipart.commons.CommonsMultipartFile


class AtividadeController {

	static allowedMethods = [save: "POST", update: "POST", delete: "POST", importar: ["POST", "GET"], import_activity: ["POST", "GET"], upload_lab: "POST", download_labs:["POST", "GET"]]

	def index() {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		redirect(action: "list", params: params)
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def list(Integer max) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		params.max = Math.min(max ?: 10, 100)
		[atividadeInstanceList: Atividade.list(params), atividadeInstanceTotal: Atividade.count()]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def create() {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		[atividadeInstance: new Atividade(params)]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def save() {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = new Atividade(params)
		if (!atividadeInstance.save(flush: true)) {
			render(view: "create", model: [atividadeInstance: atividadeInstance])
			return
		}

		flash.message = message(code: 'default.created.message', args: [
			message(code: 'atividade.label', default: 'Atividade'),
			atividadeInstance.id
		])
		redirect(action: "show", id: atividadeInstance.id)
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def show(Long id) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (!atividadeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
			return
		}

		[atividadeInstance: atividadeInstance]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def edit(Long id) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (!atividadeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
			return
		}

		[atividadeInstance: atividadeInstance]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def update(Long id, Long version) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (!atividadeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
			return
		}

		if (version != null) {
			if (atividadeInstance.version > version) {
				atividadeInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
						[
							message(code: 'atividade.label', default: 'Atividade')] as Object[],
						"Another user has updated this Atividade while you were editing")
				render(view: "edit", model: [atividadeInstance: atividadeInstance])
				return
			}
		}

		atividadeInstance.properties = params

		if (!atividadeInstance.save(flush: true)) {
			render(view: "edit", model: [atividadeInstance: atividadeInstance])
			return
		}

		flash.message = message(code: 'default.updated.message', args: [
			message(code: 'atividade.label', default: 'Atividade'),
			atividadeInstance.id
		])
		redirect(action: "show", id: atividadeInstance.id)
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def delete(Long id) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (!atividadeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
			return
		}

		try {
			atividadeInstance.delete(flush: true)
			flash.message = message(code: 'default.deleted.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
		}
		catch (DataIntegrityViolationException e) {
			flash.message = message(code: 'default.not.deleted.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "show", id: id)
		}
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def import_activity (Integer max) {
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		params.max = Math.min(max ?: 10, 100)
		[atividadeInstanceList: Atividade.list(params), atividadeInstanceTotal: Atividade.count()]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def importar(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance2 = Atividade.get(id)
		def nome = atividadeInstance2.nome
		def criterios = atividadeInstance2.criterios
		def deadline = atividadeInstance2.deadline
		def descricao = atividadeInstance2.descricao
		[atividadeInstance: new Atividade(params), nome:nome, criterios:criterios, deadline:deadline, descricao:descricao]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def editar_criterios(){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def lista_nomes_criterios = params.criterios
		def tamanho_lista
		params.criterios = null
		def atividadeInstance = new Atividade(params)
		atividadeInstance.save(failOnError:true)
		List<Especificacao> lista_criterios = new ArrayList<Especificacao>()
		if (lista_nomes_criterios == null){
			tamanho_lista = 0
			redirect(action: "show", id: atividadeInstance.id)
		}
		else if (lista_nomes_criterios instanceof String){
			tamanho_lista = lista_nomes_criterios.size()
			lista_criterios.add(Especificacao.valueOf(lista_nomes_criterios))
		}else{
			tamanho_lista = lista_nomes_criterios.size()
			for (String criterio: lista_nomes_criterios){
				lista_criterios.add(Especificacao.valueOf(criterio))
			}
		}
		[atividadeInstance: atividadeInstance,listaCriterios: lista_criterios, listaInstanceTotal: tamanho_lista, peso: 0]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def save_criterios(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (params.nome instanceof String){
			String[] nome= new String[1]
			nome[0] = params.nome
			String[] descricao= new String[1]
			descricao[0] = params.descricao
			String[] peso= new String[1]
			peso[0] = params.peso
			criaCriterios(nome, descricao, peso, id)
		}else{
			criaCriterios(params.nome, params.descricao, params.peso, id)
		}
		redirect(action: "configura_criterios", id: atividadeInstance.id)
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def configura_criterios(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		def lista_criterios = atividadeInstance.criterios
		[atividadeInstance: atividadeInstance, listaCriterios: lista_criterios]
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}

	def finaliza_criterios(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		if (params.nome instanceof String){
			String[] nome= new String[1]
			nome[0] = params.nome
			String[] key= new String[1]
			key[0] = params.key
			String[] value= new String[1]
			value[0] = params.value
			trataPropriedades(nome, key, value, id)
		}else{
			trataPropriedades(params.nome, params.key, params.value, id)
		}
		criaXml(id)
		redirect(action: "show", id: id)
		}else{
		redirect(controller:"user",action:"proibido")
		}
	}
	
	def finaliza_criterios_default(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance = Atividade.get(id)
		criaXml(id)
		redirect(action: "show", id: id)
		}else{
		redirect(controller:"user",action:"proibido")
		}
		
	}
	
	def upload_lab (Long id){
		def atividadeInstance = Atividade.get(params.id)
		def email = session.openidParams.ax.get("email").toString()
		//handle uploaded file
		def f = request.getFile('lab')
		def webRootDir = servletContext.getRealPath("/")
		def userDir = new File(webRootDir, "/labs/${atividadeInstance.nome}/${email.substring(1,email.length()-1)}")
		if (! userDir.exists()){
			userDir.mkdirs()
		}else{
			userDir.deleteDir()
			userDir.mkdirs()
		}
		f.transferTo( new File( userDir, f.originalFilename))
		render "Atividade enviada com sucesso!"
		redirect(action: "show_aluno", id: id)
		
	}
	
	
	def upload_error ={
		
	}
	
	def download_lab={
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		File f = new File("./web-app/labs")
		File[] arquivos = f.listFiles()
		
		int tamanho;
		if (f == null){
			tamanho=0;
		}else{
		tamanho = arquivos.length
		}


		[files:arquivos, atividadeInstanceTotal:tamanho]
		}else{
		redirect(controller:"user",action:"proibido")
		}
		
	}
	
	def download_labs(String caminho){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		File arquivo = new File(caminho)
				
					if (arquivo.getName().contains(".zip")){
							 response.setContentType("application/octet-stream")
							 response.setHeader("Content-disposition", "attachment;filename=${arquivo.getName()}")
							 response.outputStream << arquivo.newInputStream()  //<-- ask the user to download
		
				
				}
		File[] arquivos = arquivo.listFiles()
		int tamanho;
		if (arquivos==null){
			tamanho = 0
			redirect(action:"download_lab")
		}else{
		tamanho = arquivos.length
		}

		[files:arquivos, atividadeInstanceTotal: tamanho]
		}else{
		redirect(controller:"user",action:"proibido")
		}
		
	}
	
	def show_aluno(Long id) {
		def atividadeInstance = Atividade.get(id)
		if (!atividadeInstance) {
			flash.message = message(code: 'default.not.found.message', args: [
				message(code: 'atividade.label', default: 'Atividade'),
				id
			])
			redirect(action: "list")
			return
		}

		[atividadeInstance: atividadeInstance]
	}
	
	def analyze(Long id){
		if (Verifica.verificaProf(session.openidParams.ax.get("email").toString())){
		def atividadeInstance2 = Atividade.get(id)
		def activityPath = "./web-app/labs/" + atividadeInstance2.nome
		def criterios = atividadeInstance2.criterios
		SimpleChecker sc = new SimpleChecker()
		sc.runCheckerForAllActivities(activityPath, criterios.toString());
		redirect(action:"list")
		}else{
		redirect(controller:"user",action:"proibido")
		}

		}

	
	

	private void criaCriterios(String[] nome, String[] descricao, String[] peso, Long id){
		def atividInstance = Atividade.get(id)
		for (int i =0; i < nome.length; i++){
			def criterioInstance = new Criterio(Especificacao.valueOf(nome[i].replace(" ", "")), Double.parseDouble(peso[i]), null)
			if(nome[i].equals("Strict Duplicate Code")){
				criterioInstance.propriedades = ["min": "12"]
			}else if (nome[i].equals("Boolean Expression Complexity")){
				criterioInstance.propriedades = ["max": "3"]
			} else if (nome[i].equals("Class Data Abstraction Coupling")){
				criterioInstance.propriedades = ["max": "7"]
			}else if (nome[i].equals("Class FanOut Complexity")){
				criterioInstance.propriedades = ["max": "20"]
			}else if (nome[i].equals("Cyclomatic Complexity")){
				criterioInstance.propriedades = ["max": "10"]
			}else if (nome[i].equals("NPath Complexity")){
				criterioInstance.propriedades = ["max": "200"]
			}else if (nome[i].equals("File Length")){
				criterioInstance.propriedades = ["max": "2000"]
			}else if (nome[i].equals("Method Count")){
				criterioInstance.propriedades = ["maxTotal": "100"]
			}else if (nome[i].equals("Java NCSS")){
				criterioInstance.propriedades = ["methodMaximum": "50", "classMaximum": "1500", "fileMaximum": "2000"]
			}else if (nome[i].equals("Indentation")){
				criterioInstance.propriedades = ["caseIndent": "4","basicOffset": "4","braceAdjustment": "0"]
			}else if (nome[i].equals("Local Variable Name")){
				criterioInstance.propriedades = ["format": '^[a-z][a-zA-Z0-9]*$', "tokens": "VARIABLE_DEF,PARAMETERS_DEF"]
			}else if (nome[i].equals("Method Name")){
				criterioInstance.propriedades = ["format": '^[a-z][a-zA-Z0-9]*$']
			}else if (nome[i].equals("Executable Statement Count")){
				criterioInstance.propriedades = ["max": "30", "tokens": "CTOR_DEF, METHOD_DEF, INSTANCE_INIT, STATIC_INIT"]
			}else if (nome[i].equals("Line Length")){
				criterioInstance.propriedades = ["max": "80", "ignorePattern": '^$']
			}else if (nome[i].equals("Method Length")){
				criterioInstance.propriedades = ["max": "150", "tokens": "METHOD_DEF, CTOR_DEF", "countEmpty": "true"]
			}else if (nome[i].equals("Parameter Number")){
				criterioInstance.propriedades = ["max": "7", "tokens": "METHOD_DEF, CTOR_DEF"]
			}
			atividInstance.addToCriterios(criterioInstance)
			atividInstance.save(failOnError:true)
		}
	}


	private void trataPropriedades(String[] nomeCriterio, String[] key, String[] value, Long id){
		if (nomeCriterio != null){
			def atInstance = Atividade.get(id)
			Criterio[] lista_criterios = atInstance.criterios.toArray()
			for (int i = 0; i < lista_criterios.length;i++){
				def item = lista_criterios[i]
				for (int j= 0; j < nomeCriterio.length; j++){
					if (item.especificacao.nome.equals(nomeCriterio[j])){
						for (Iterator it = item.propriedades.entrySet().iterator(); it.hasNext();){
							Map.Entry entry = (Map.Entry) it.next()
							if (key[j].equals(entry.getKey())){
								entry.setValue(value[j])
							}
						}
					}
				}
			}
			atInstance.save(failOnError:true)
		}
	}

	private void criaXml(Long id){
		def atividadeInstance = Atividade.get(id)
		def writer = new FileWriter(new File("Criterios_" + atividadeInstance.nome.replace(" ", "_") + ".xml"))
		def builder = new StreamingMarkupBuilder()
		def check = builder.bind({
			mkp.xmlDeclaration()

			module(name:"Checker"){
				property(name:"severity", value:"error")


				module(name:"FileLenght"){
					property(name:"fileExtensions", value:"java")
				}

				module(name:"TreeWalker"){
					for (Criterio c: atividadeInstance.criterios){
						if (!c.propriedades.isEmpty()){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
								for (Iterator iter = c.propriedades.entrySet().iterator(); iter.hasNext();){
									Map.Entry entry = (Map.Entry) iter.next()
									property(name: "" + entry.getKey(), value:"" + entry.getValue())
								}
							}
						}else if (c.especificacao.nome.equals('Missing Override')){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
							property(name: "javaFiveCompatibility", value: "true")
							}
						}else if (c.especificacao.nome.equals('Visibility Modifier')){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
							property(name: "packageAllowed", value: "false")
							}
						}else if (c.especificacao.nome.equals('Javadoc Type')){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
							property(name: "scope", value: "public")
							}
						}else if (c.especificacao.nome.equals('Javadoc Method')){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
							property(name: "scope", value: "public")
							property(name: "allowUndeclaredRTE", value: "true")
							property(name: "allowThrowsTagsForSubclasses", value: "true")
							}
						}else if (c.especificacao.nome.equals('Javadoc Style')){
							module(name: "" + c.especificacao.nome.replace(" ", "")){
							property(name: "scope", value: "public")
							property(name: "checkEmptyJavadoc", value: "true")
							property(name: "checkFirstSentence", value: "false")
							}
						}else if (c.especificacao.nome.equals("Redundant Import") || (c.especificacao.nome.equals("Unused Imports"))){
							module(name: "" + c.especificacao.nome.replace(" ", ""))
						}
					}
				}
			}
		})
		XmlUtil.serialize(check,writer)
	}
}
